/**
  ******************************************************************************
  * @file    sddriver.c
  * @brief   SD Disk I/O driver
  ******************************************************************************
  * @attention
  *
  ******************************************************************************
  */
#include "bsp.h"
#include "tskcfg.h"
#include "diskio.h"
#include "sdiobsp.h"
#include <string.h>
#include <stdio.h>

/**
  * @brief  Returns the current card's status.
  * @param  p_card_status: pointer to the buffer that will contain the SD card
  *         status (Card Status register)
  * @retval error state
  */
static uint32_t sd_get_csr(SD_CardInfoType *sdinfo, uint32_t *pcsr)
{
  uint32_t errorstate;

  if(pcsr == NULL)
  {
    return SDMMC_ERROR_INVALID_PARAMETER;
  }

  /* Send Status command */
  errorstate = SDMMC_CmdSendStatus(SDIO, (uint32_t)(sdinfo->rca << 16U));
  if(errorstate != SDMMC_ERROR_NONE)
  {
    return errorstate;
  }

  /* Get SD card status */
  *pcsr = SDIO_GetResponse(SDIO, SDIO_RESP1);

  return SDMMC_ERROR_NONE;
}

/**
  * @brief  Gets the current sd card data state.
  * @param  hsd: pointer to SD handle
  * @retval Card state
  */
static SD_CardCSRStateEnum sd_get_csr_state(SD_CardInfoType *sdinfo)
{
  uint32_t csrstate;
  uint32_t errorstate;
  uint32_t csr = 0;

  errorstate = sd_get_csr(sdinfo, &csr);
  if(errorstate != SDMMC_ERROR_NONE)
  {
    sdinfo->error_code |= errorstate;
  }

  csrstate = ((csr >> 9U) & 0x0FU);

  return csrstate;
}

/**
  * @brief  Enquires cards about their operating voltage and configures clock
  *         controls and stores SD information that will be needed in future
  *         in the SD handle.
  * @param  hsd: Pointer to SD handle
  * @retval error state
  */
static uint32_t sd_power_on(SD_CardInfoType *sdinfo)
{
  __IO uint32_t count = 0u;
  uint32_t response = 0u, validvoltage = 0u;
  uint32_t errorstate;

  /* CMD0: GO_IDLE_STATE */
  errorstate = SDMMC_CmdGoIdleState(SDIO);
  if(errorstate != SDMMC_ERROR_NONE)
  {
    return errorstate;
  }

  /* CMD8: SEND_IF_COND: Command available only on V2.0 cards */
  errorstate = SDMMC_CmdOperCond(SDIO);
  if(errorstate != SDMMC_ERROR_NONE)
  {
    sdinfo->version = CARD_V1_X;
    
    /* CMD0: GO_IDLE_STATE */
    errorstate = SDMMC_CmdGoIdleState(SDIO);
    if(errorstate != SDMMC_ERROR_NONE)
    {
      return errorstate;
    }
  }
  else
  {
    sdinfo->version = CARD_V2_X;
  }

  if(sdinfo->version == CARD_V2_X)
  {
    /* SEND CMD55 APP_CMD with RCA as 0 */
    errorstate = SDMMC_CmdAppCommand(SDIO, 0);
    if(errorstate != SDMMC_ERROR_NONE)
    {
      return SDMMC_ERROR_UNSUPPORTED_FEATURE;
    }
  }
  
  /* SD CARD */
  /* Send ACMD41 SD_APP_OP_COND with Argument 0x80100000 */
  while((count < SDMMC_MAX_VOLT_TRIAL) && (validvoltage == 0u))
  {
    /* SEND CMD55 APP_CMD with RCA as 0 */
    errorstate = SDMMC_CmdAppCommand(SDIO, 0);
    if(errorstate != SDMMC_ERROR_NONE)
    {
      return errorstate;
    }

    /* Send CMD41 */
    errorstate = SDMMC_CmdAppOperCommand(SDIO, SDMMC_VOLTAGE_WINDOW_SD | SDMMC_HIGH_CAPACITY | SD_SWITCH_1_8V_CAPACITY);
    if(errorstate != SDMMC_ERROR_NONE)
    {
      return SDMMC_ERROR_UNSUPPORTED_FEATURE;
    }

    /* Get command response */
    response = SDIO_GetResponse(SDIO, SDIO_RESP1);

    /* Get operating voltage*/
    validvoltage = (((response >> 31U) == 1U) ? 1U : 0u);

    count++;
  }

  if(count >= SDMMC_MAX_VOLT_TRIAL)
  {
    return SDMMC_ERROR_INVALID_VOLTRANGE;
  }

  if((response & SDMMC_HIGH_CAPACITY) == SDMMC_HIGH_CAPACITY) /* (response &= SD_HIGH_CAPACITY) */
  {
    sdinfo->type = CARD_SDHC_SDXC;
  }
  else
  {
    sdinfo->type = CARD_SDSC;
  }

  return SDMMC_ERROR_NONE;
}

/**
  * @brief  Returns information the information of the card which are stored on
  *         the CSD register.
  * @param  hsd: Pointer to SD handle
  * @param  pcsd: Pointer to a HAL_SD_CardCSDTypeDef structure that  
  *         contains all CSD register parameters
  * @retval null
  */
static uint8_t sd_get_card_csd(SD_CardInfoType *sdinfo)
{
  sdinfo->csd.CSDStruct       = (uint8_t)((sdinfo->csd_buf[0] & 0xC0000000U) >> 30U);
  sdinfo->csd.SysSpecVersion  = (uint8_t)((sdinfo->csd_buf[0] & 0x3C000000U) >> 26U);
  sdinfo->csd.Reserved1       = (uint8_t)((sdinfo->csd_buf[0] & 0x03000000U) >> 24U);
  sdinfo->csd.TAAC            = (uint8_t)((sdinfo->csd_buf[0] & 0x00FF0000U) >> 16U);
  sdinfo->csd.NSAC            = (uint8_t)((sdinfo->csd_buf[0] & 0x0000FF00U) >> 8U);
  sdinfo->csd.MaxBusClkFrec   = (uint8_t)(sdinfo->csd_buf[0] & 0x000000FFU);
  sdinfo->csd.CardComdClasses = (uint16_t)((sdinfo->csd_buf[1] & 0xFFF00000U) >> 20U);
  sdinfo->csd.RdBlockLen      = (uint8_t)((sdinfo->csd_buf[1] & 0x000F0000U) >> 16U);
  sdinfo->csd.PartBlockRead   = (uint8_t)((sdinfo->csd_buf[1] & 0x00008000U) >> 15U);
  sdinfo->csd.WrBlockMisalign = (uint8_t)((sdinfo->csd_buf[1] & 0x00004000U) >> 14U);
  sdinfo->csd.RdBlockMisalign = (uint8_t)((sdinfo->csd_buf[1] & 0x00002000U) >> 13U);
  sdinfo->csd.DSRImpl         = (uint8_t)((sdinfo->csd_buf[1] & 0x00001000U) >> 12U);
  sdinfo->csd.Reserved2       = 0U; /*!< Reserved */

  if(sdinfo->type == CARD_SDSC)
  {
    sdinfo->csd.DeviceSize         = (((sdinfo->csd_buf[1] & 0x000003FFU) << 2U) | ((sdinfo->csd_buf[2] & 0xC0000000U) >> 30U));
    sdinfo->csd.MaxRdCurrentVDDMin = (uint8_t)((sdinfo->csd_buf[2] & 0x38000000U) >> 27U);
    sdinfo->csd.MaxRdCurrentVDDMax = (uint8_t)((sdinfo->csd_buf[2] & 0x07000000U) >> 24U);
    sdinfo->csd.MaxWrCurrentVDDMin = (uint8_t)((sdinfo->csd_buf[2] & 0x00E00000U) >> 21U);
    sdinfo->csd.MaxWrCurrentVDDMax = (uint8_t)((sdinfo->csd_buf[2] & 0x001C0000U) >> 18U);
    sdinfo->csd.DeviceSizeMul      = (uint8_t)((sdinfo->csd_buf[2] & 0x00038000U) >> 15U);

    sdinfo->block_nbr  = (sdinfo->csd.DeviceSize + 1U) ;
    sdinfo->block_nbr *= (1UL << ((sdinfo->csd.DeviceSizeMul & 0x07U) + 2U));
    sdinfo->block_size = (1UL << (sdinfo->csd.RdBlockLen & 0x0FU));

    sdinfo->logical_block_nbr  = (sdinfo->block_nbr) * ((sdinfo->block_size) / 512U);
    sdinfo->logical_block_size = 512U;
  }
  else if(sdinfo->type == CARD_SDHC_SDXC)
  {
    /* Byte 7 */
    sdinfo->csd.DeviceSize = (((sdinfo->csd_buf[1] & 0x0000003FU) << 16U) | ((sdinfo->csd_buf[2] & 0xFFFF0000U) >> 16U));

    sdinfo->block_nbr = ((sdinfo->csd.DeviceSize + 1U) * 1024U);
    sdinfo->logical_block_nbr = sdinfo->block_nbr;
    sdinfo->block_size = 512U;
    sdinfo->logical_block_size = sdinfo->block_size;
  }
  else
  {
    __SDIO_CLEAR_FLAG(SDIO, SDIO_STATIC_FLAGS);  /* Clear all the static flags */
    
    sdinfo->error_code |= SDMMC_ERROR_UNSUPPORTED_FEATURE;
    
    return 1;
  }

  sdinfo->csd.EraseGrSize         = (uint8_t)((sdinfo->csd_buf[2] & 0x00004000U) >> 14U);
  sdinfo->csd.EraseGrMul          = (uint8_t)((sdinfo->csd_buf[2] & 0x00003F80U) >> 7U);
  sdinfo->csd.WrProtectGrSize     = (uint8_t)(sdinfo->csd_buf[2] & 0x0000007FU);
  sdinfo->csd.WrProtectGrEnable   = (uint8_t)((sdinfo->csd_buf[3] & 0x80000000U) >> 31U);
  sdinfo->csd.ManDeflECC          = (uint8_t)((sdinfo->csd_buf[3] & 0x60000000U) >> 29U);
  sdinfo->csd.WrSpeedFact         = (uint8_t)((sdinfo->csd_buf[3] & 0x1C000000U) >> 26U);
  sdinfo->csd.MaxWrBlockLen       = (uint8_t)((sdinfo->csd_buf[3] & 0x03C00000U) >> 22U);
  sdinfo->csd.WriteBlockPaPartial = (uint8_t)((sdinfo->csd_buf[3] & 0x00200000U) >> 21U);
  sdinfo->csd.Reserved3           = 0;
  sdinfo->csd.ContentProtectAppli = (uint8_t)((sdinfo->csd_buf[3] & 0x00010000U) >> 16U);
  sdinfo->csd.FileFormatGroup     = (uint8_t)((sdinfo->csd_buf[3] & 0x00008000U) >> 15U);
  sdinfo->csd.CopyFlag            = (uint8_t)((sdinfo->csd_buf[3] & 0x00004000U) >> 14U);
  sdinfo->csd.PermWrProtect       = (uint8_t)((sdinfo->csd_buf[3] & 0x00002000U) >> 13U);
  sdinfo->csd.TempWrProtect       = (uint8_t)((sdinfo->csd_buf[3] & 0x00001000U) >> 12U);
  sdinfo->csd.FileFormat          = (uint8_t)((sdinfo->csd_buf[3] & 0x00000C00U) >> 10U);
  sdinfo->csd.ECC                 = (uint8_t)((sdinfo->csd_buf[3] & 0x00000300U) >> 8U);
  sdinfo->csd.CSD_CRC             = (uint8_t)((sdinfo->csd_buf[3] & 0x000000FEU) >> 1U);
  sdinfo->csd.Reserved4           = 1;

  return 0;
}

/**
  * @brief  Initializes the sd card.
  * @param  hsd: Pointer to SD handle
  * @retval SD Card error state
  */
static uint32_t sd_initializes_card(SD_CardInfoType *sdinfo)
{
  uint32_t errorstate;
  uint16_t sd_rca = 1u;

  /* Check the power State */
  if((SDIO->POWER & SDIO_POWER_PWRCTRL) == 0u)
  {
    /* Power off */
    return SDMMC_ERROR_REQUEST_NOT_APPLICABLE;
  }

  if(sdinfo->type != CARD_SECURED)
  {
    /* Send CMD2 ALL_SEND_CID */
    errorstate = SDMMC_CmdSendCID(SDIO);
    if(errorstate != SDMMC_ERROR_NONE)
    {
      return errorstate;
    }
    else
    {
      /* Get Card identification number data */
      sdinfo->cid_buf[0u] = SDIO_GetResponse(SDIO, SDIO_RESP1);
      sdinfo->cid_buf[1u] = SDIO_GetResponse(SDIO, SDIO_RESP2);
      sdinfo->cid_buf[2u] = SDIO_GetResponse(SDIO, SDIO_RESP3);
      sdinfo->cid_buf[3u] = SDIO_GetResponse(SDIO, SDIO_RESP4);
    }
  }

  if(sdinfo->type != CARD_SECURED)
  {
    /* Send CMD3 SET_REL_ADDR with argument 0 */
    /* SD Card publishes its RCA. */
    errorstate = SDMMC_CmdSetRelAdd(SDIO, &sd_rca);
    if(errorstate != SDMMC_ERROR_NONE)
    {
      return errorstate;
    }
  }
  
  if(sdinfo->type != CARD_SECURED)
  {
    /* Get the SD card RCA */
    sdinfo->rca = sd_rca;

    /* Send CMD9 SEND_CSD with argument as card's RCA */
    errorstate = SDMMC_CmdSendCSD(SDIO, (uint32_t)(sdinfo->rca << 16u));
    if(errorstate != SDMMC_ERROR_NONE)
    {
      return errorstate;
    }
    else
    {
      /* Get Card Specific Data */
      sdinfo->csd_buf[0u] = SDIO_GetResponse(SDIO, SDIO_RESP1);
      sdinfo->csd_buf[1u] = SDIO_GetResponse(SDIO, SDIO_RESP2);
      sdinfo->csd_buf[2u] = SDIO_GetResponse(SDIO, SDIO_RESP3);
      sdinfo->csd_buf[3u] = SDIO_GetResponse(SDIO, SDIO_RESP4);
    }
  }

  /* Get the Card Class */
  sdinfo->sclass = (SDIO_GetResponse(SDIO, SDIO_RESP2) >> 20u);

  /* Get CSD parameters */
  if(sd_get_card_csd(sdinfo) != 0)
  {
    return SDMMC_ERROR_UNSUPPORTED_FEATURE;
  }

  /* Select the Card */
  errorstate = SDMMC_CmdSelDesel(SDIO, (uint32_t)(((uint32_t)sdinfo->rca) << 16u));
  if(errorstate != SDMMC_ERROR_NONE)
  {
    return errorstate;
  }
  
  /* Configure SDIO peripheral interface */
  (void)SDIO_Init(SDIO, sdinfo->init_4b);

  /* All cards are initialized */
  return SDMMC_ERROR_NONE;
}

/**
  * @brief  Finds the SD card SCR register value.
  * @param  sdinfo: Pointer to SD handle
  * @param  pcsr: pointer to the buffer that will contain the SCR value
  * @retval error state
  */
static uint32_t sd_find_scr(SD_CardInfoType *sdinfo, uint32_t *pcsr)
{
  SDIO_DataInitTypeDef config;
  uint32_t errorstate;
  uint32_t tickstart = HAL_GetTick();
  uint32_t index = 0U;
  uint32_t tempscr[2U] = {0U, 0U};
  uint32_t *scr = pcsr;

  /* Set Block Size To 8 Bytes */
  errorstate = SDMMC_CmdBlockLength(SDIO, 8U);
  if(errorstate != SDMMC_ERROR_NONE)
  {
    return errorstate;
  }

  /* Send CMD55 APP_CMD with argument as card's RCA */
  errorstate = SDMMC_CmdAppCommand(SDIO, (uint32_t)((sdinfo->rca) << 16U));
  if(errorstate != SDMMC_ERROR_NONE)
  {
    return errorstate;
  }

  config.DataTimeOut   = SDMMC_DATATIMEOUT;
  config.DataLength    = 8U;
  config.DataBlockSize = SDIO_DATABLOCK_SIZE_8B;
  config.TransferDir   = SDIO_TRANSFER_DIR_TO_SDIO;
  config.TransferMode  = SDIO_TRANSFER_MODE_BLOCK;
  config.DPSM          = SDIO_DPSM_ENABLE;
  (void)SDIO_ConfigData(SDIO, &config);

  /* Send ACMD51 SD_APP_SEND_SCR with argument as 0 */
  errorstate = SDMMC_CmdSendSCR(SDIO);
  if(errorstate != SDMMC_ERROR_NONE)
  {
    return errorstate;
  }

  while(!__SDIO_GET_FLAG(SDIO, SDIO_FLAG_RXOVERR | SDIO_FLAG_DCRCFAIL | SDIO_FLAG_DTIMEOUT))
  {
    if(__SDIO_GET_FLAG(SDIO, SDIO_FLAG_RXDAVL))
    {
      *(tempscr + index) = SDIO_ReadFIFO(SDIO);
      index++;
    }
    else if(!__SDIO_GET_FLAG(SDIO, SDIO_FLAG_RXACT))
    {
      break;
    }

    if((HAL_GetTick() - tickstart) >= SDMMC_SWDATATIMEOUT)
    {
      return SDMMC_ERROR_TIMEOUT;
    }
  }

#if defined(SDIO_STA_STBITERR)
  if(__HAL_SD_GET_FLAG(hsd, SDIO_FLAG_DTIMEOUT) || (__HAL_SD_GET_FLAG(hsd, SDIO_FLAG_STBITERR)))
#else /* SDIO_STA_STBITERR not defined */
  if(__SDIO_GET_FLAG(SDIO, SDIO_FLAG_DTIMEOUT))
#endif /* SDIO_STA_STBITERR */
  {
    __SDIO_CLEAR_FLAG(SDIO, SDIO_FLAG_DTIMEOUT);

    return SDMMC_ERROR_DATA_TIMEOUT;
  }
  else if(__SDIO_GET_FLAG(SDIO, SDIO_FLAG_DCRCFAIL))
  {
    __SDIO_CLEAR_FLAG(SDIO, SDIO_FLAG_DCRCFAIL);

    return SDMMC_ERROR_DATA_CRC_FAIL;
  }
  else if(__SDIO_GET_FLAG(SDIO, SDIO_FLAG_RXOVERR))
  {
    __SDIO_CLEAR_FLAG(SDIO, SDIO_FLAG_RXOVERR);

    return SDMMC_ERROR_RX_OVERRUN;
  }
  else
  {
    /* No error flag set */
    /* Clear all the static flags */
    __SDIO_CLEAR_FLAG(SDIO, SDIO_STATIC_DATA_FLAGS);

    *scr = (((tempscr[1] & SDMMC_0TO7BITS) << 24)  | ((tempscr[1] & SDMMC_8TO15BITS) << 8) |\
            ((tempscr[1] & SDMMC_16TO23BITS) >> 8) | ((tempscr[1] & SDMMC_24TO31BITS) >> 24));
    scr++;
    *scr = (((tempscr[0] & SDMMC_0TO7BITS) << 24)  | ((tempscr[0] & SDMMC_8TO15BITS) << 8) |\
            ((tempscr[0] & SDMMC_16TO23BITS) >> 8) | ((tempscr[0] & SDMMC_24TO31BITS) >> 24));
  }

  return SDMMC_ERROR_NONE;
}

/**
  * @brief  Enables the SDIO wide bus mode.
  * @param  hsd: pointer to SD handle
  * @retval error state
  */
static uint32_t sd_widebus_enable(SD_CardInfoType *sdinfo)
{
  uint32_t scr[2U] = {0U, 0U};
  uint32_t errorstate;

  if((SDIO_GetResponse(SDIO, SDIO_RESP1) & SDMMC_CARD_LOCKED) == SDMMC_CARD_LOCKED)
  {
    return SDMMC_ERROR_LOCK_UNLOCK_FAILED;
  }

  /* Get SCR Register */
  errorstate = sd_find_scr(sdinfo, scr);
  if(errorstate != SDMMC_ERROR_NONE)
  {
    return errorstate;
  }

  /* If requested card supports wide bus operation */
  if((scr[1U] & SDMMC_WIDE_BUS_SUPPORT) != SDMMC_ALLZERO)
  {
    /* Send CMD55 APP_CMD with argument as card's RCA.*/
    errorstate = SDMMC_CmdAppCommand(SDIO, (uint32_t)(sdinfo->rca << 16U));
    if(errorstate != SDMMC_ERROR_NONE)
    {
      return errorstate;
    }

    /* Send ACMD6 APP_CMD with argument as 2 for wide bus mode */
    errorstate = SDMMC_CmdBusWidth(SDIO, 2U);
    if(errorstate != SDMMC_ERROR_NONE)
    {
      return errorstate;
    }

    return SDMMC_ERROR_NONE;
  }
  else
  {
    return SDMMC_ERROR_REQUEST_NOT_APPLICABLE;
  }
}

/**
  * @brief  Disables the SDIO wide bus mode.
  * @param  hsd: Pointer to SD handle
  * @retval error state
  */
static uint32_t sd_widebus_disable(SD_CardInfoType *sdinfo)
{
  uint32_t scr[2u] = {0u, 0u};
  uint32_t errorstate;

  if((SDIO_GetResponse(SDIO, SDIO_RESP1) & SDMMC_CARD_LOCKED) == SDMMC_CARD_LOCKED)
  {
    return SDMMC_ERROR_LOCK_UNLOCK_FAILED;
  }

  /* Get SCR Register */
  errorstate = sd_find_scr(sdinfo, scr);
  if(errorstate != SDMMC_ERROR_NONE)
  {
    return errorstate;
  }

  /* If requested card supports 1 bit mode operation */
  if((scr[1u] & SDMMC_SINGLE_BUS_SUPPORT) != SDMMC_ALLZERO)
  {
    /* Send CMD55 APP_CMD with argument as card's RCA */
    errorstate = SDMMC_CmdAppCommand(SDIO, (uint32_t)(sdinfo->rca << 16u));
    if(errorstate != SDMMC_ERROR_NONE)
    {
      return errorstate;
    }

    /* Send ACMD6 APP_CMD with argument as 0 for single bus mode */
    errorstate = SDMMC_CmdBusWidth(SDIO, 0u);
    if(errorstate != SDMMC_ERROR_NONE)
    {
      return errorstate;
    }

    return SDMMC_ERROR_NONE;
  }
  else
  {
    return SDMMC_ERROR_REQUEST_NOT_APPLICABLE;
  }
}

/**
  * @brief  Enables wide bus operation for the requested card if supported by
  *         card.
  * @param  hsd: Pointer to SD handle
  * @param  WideMode: Specifies the SD card wide bus mode
  *          This parameter can be one of the following values:
  *            @arg SDIO_BUS_WIDE_8B: 8-bit data transfer
  *            @arg SDIO_BUS_WIDE_4B: 4-bit data transfer
  *            @arg SDIO_BUS_WIDE_1B: 1-bit data transfer
  * @retval HAL status
  */
HAL_StatusTypeDef sd_config_wide_bus_operation(SD_CardInfoType *sdinfo, uint32_t widemode)
{
  SDIO_InitTypeDef init;
  uint32_t errorstate;
  HAL_StatusTypeDef status = HAL_OK;

  if(sdinfo->type != CARD_SECURED)
  {
    if(widemode == SDIO_BUS_WIDE_8B)
    {
      sdinfo->error_code |= SDMMC_ERROR_UNSUPPORTED_FEATURE;
    }
    else if(widemode == SDIO_BUS_WIDE_4B)
    {
      errorstate = sd_widebus_enable(sdinfo);

      sdinfo->error_code |= errorstate;
    }
    else if(widemode == SDIO_BUS_WIDE_1B)
    {
      errorstate = sd_widebus_disable(sdinfo);

      sdinfo->error_code |= errorstate;
    }
    else
    {
      /* WideMode is not a valid argument*/
      sdinfo->error_code |= SDMMC_ERROR_INVALID_PARAMETER;
    }
  }
  else
  {
    /* MMC Card does not support this feature */
    sdinfo->error_code |= SDMMC_ERROR_UNSUPPORTED_FEATURE;
  }

  if(sdinfo->error_code != SDMMC_ERROR_NONE)
  {
    /* Clear all the static flags */
    __SDIO_CLEAR_FLAG(SDIO, SDIO_STATIC_FLAGS);

    status = HAL_ERROR;
  }
  else
  {
    /* Configure the SDIO peripheral */
    init.ClockEdge           = sdinfo->init_4b.ClockEdge;
    init.ClockBypass         = sdinfo->init_4b.ClockBypass;
    init.ClockPowerSave      = sdinfo->init_4b.ClockPowerSave;
    init.BusWide             = widemode;
    init.HardwareFlowControl = sdinfo->init_4b.HardwareFlowControl;
    init.ClockDiv            = sdinfo->init_4b.ClockDiv;
    (void)SDIO_Init(SDIO, init);
  }

  /* Set Block Size for Card */
  errorstate = SDMMC_CmdBlockLength(SDIO, BLOCKSIZE);
  if(errorstate != SDMMC_ERROR_NONE)
  {
    /* Clear all the static flags */
    __SDIO_CLEAR_FLAG(SDIO, SDIO_STATIC_FLAGS);
    sdinfo->error_code |= errorstate;
    status = HAL_ERROR;
  }
  
  return status;
}

static void sd_message_queue_create(void)
{
}

/**
  * @brief  Initializes a Drive
  * @param  lun : not used
  * @retval DSTATUS: Operation status
  */
uint8_t sd_diskio_initialize(SD_CardInfoType *sdinfo)
{
  uint32_t errorstate;
  uint32_t cardstate;
  
  if(sd_card_detect() != SDCARD_DETECTED)
  {
    return STA_NODISK;
  }
  
/**** init io, clock ****/

/************************************************************************** */
  if(SDIO_Init(SDIO, sdinfo->init_1b) != 0)
  {
    return STA_NOINIT;
  }
  
  /* Disable SDIO Clock */
  __SDIO_DISABLE(SDIO);

  /* Set Power State to ON */
  SDIO->POWER = SDIO_POWER_PWRCTRL;

  /* Enable SDIO Clock */
  __SDIO_ENABLE(SDIO);

  /* SD卡启动时间延迟,需要测试 */
  vTaskDelay(2);

  /* Identify card operating voltage */
  errorstate = sd_power_on(sdinfo);
  if(errorstate != SDMMC_ERROR_NONE)
  {
    sdinfo->error_code |= errorstate;
    return STA_NOINIT;
  }

  /* Card initialization */
  errorstate = sd_initializes_card(sdinfo);
  if(errorstate != SDMMC_ERROR_NONE)
  {
    sdinfo->error_code |= errorstate;
    return STA_NOINIT;
  }

  /* Set Block Size for Card */
  errorstate = SDMMC_CmdBlockLength(SDIO, BLOCKSIZE);
  if(errorstate != SDMMC_ERROR_NONE)
  {
    /* Clear all the static flags */
    __SDIO_CLEAR_FLAG(SDIO, SDIO_STATIC_FLAGS);
    sdinfo->error_code |= errorstate;
    return STA_NOINIT;
  }

  sdinfo->error_code = SDMMC_ERROR_NONE;
/* ************************************************************************* */

  if(sd_config_wide_bus_operation(sdinfo, SDIO_BUS_WIDE_4B) != 0)
  {
    return STA_NOINIT;
  }
  
  cardstate = sd_get_csr_state(sdinfo);
  if(cardstate != SD_CSR_STATE_TRANSFER)
  {
    return STA_NOINIT;
  }
  
  sd_message_queue_create();
  
  return 0;
}

/**
  * @brief  Gets Disk Status
  * @param  lun : not used
  * @retval DSTATUS: Operation status
  */
uint8_t sd_diskio_status(SD_CardInfoType *sdinfo)
{
  if(sd_card_detect() != SDCARD_DETECTED)
  {
    return STA_NODISK;
  }
  
  if(sd_get_csr_state(sdinfo) != SD_CSR_STATE_TRANSFER)
  {
    return STA_NOINIT;
  }

  return 0;
}

uint8_t sd_diskio_check_status_with_timeout(SD_CardInfoType *sdinfo, uint32_t timeout)
{
  while(timeout-- > 0)
  {
    if(sd_get_csr_state(sdinfo) == SD_CSR_STATE_TRANSFER)
    {
      return 0;
    }
    
    vTaskDelay(1);
  }

  return 1;
}

/**
  * @brief  Reads block(s) from a specified address in a card. The Data transfer
  *         is managed by DMA mode.
  * @note   This API should be followed by a check on the card state through
  *         HAL_SD_GetCardState().
  * @note   You could also check the DMA transfer process through the SD Rx
  *         interrupt event.
  * @param  sdinfo: Pointer SD handle
  * @param  pdata: Pointer to the buffer that will contain the received data
  * @param  blockaddr: Block Address from where data is to be read
  * @param  numofblocks: Number of blocks to read.
  * @retval HAL status
  */
uint8_t sd_diskio_read_blocks(SD_CardInfoType *sdinfo, uint8_t *pdata, uint32_t blockaddr, uint32_t numofblocks)
{
  SDIO_DataInitTypeDef config;
  SD_DriverMessageStru msg;
  uint32_t errorstate;
  uint32_t addr = blockaddr;
  uint8_t ret;

  ret = 0;

  /* check parameter */
  if(NULL == pdata)
  {
    sdinfo->error_code |= SDMMC_ERROR_INVALID_PARAMETER;
    return 1;
  }

  sdinfo->error_code = SDMMC_ERROR_NONE;

  if((addr + numofblocks) > (sdinfo->logical_block_nbr))
  {
    sdinfo->error_code |= SDMMC_ERROR_ADDR_OUT_OF_RANGE;
    return 2;
  }

  /* initialize data control register */
  SDIO->DCTRL = 0u;

  /* enable SDIO interrupts */
  __SDIO_ENABLE_IT(SDIO, (SDIO_IT_DCRCFAIL | SDIO_IT_DTIMEOUT | SDIO_IT_RXOVERR | SDIO_IT_DATAEND));

/******* DMA Set ******/
  LL_DMA_SetDataTransferDirection(DMA2, LL_DMA_STREAM_3, LL_DMA_DIRECTION_PERIPH_TO_MEMORY);
  LL_DMA_ConfigAddresses(DMA2, LL_DMA_STREAM_3, (uint32_t)&SDIO->FIFO, (uint32_t)pdata, LL_DMA_DIRECTION_PERIPH_TO_MEMORY);
  LL_DMA_SetDataLength(DMA2, LL_DMA_STREAM_3, (uint32_t)(BLOCKSIZE * numofblocks)/4u);

  /* clear all interrupt flags at correct offset within the register */
  WRITE_REG(DMA2->LIFCR , DMA_LIFCR_CTCIF3 | DMA_LIFCR_CHTIF3 | DMA_LIFCR_CTEIF3 | DMA_LIFCR_CDMEIF3 | DMA_LIFCR_CFEIF3);
  /* enable common interrupts and fifo error interrupt */
  SET_BIT(((DMA_Stream_TypeDef *)((uint32_t)((uint32_t)DMA2 + STREAM_OFFSET_TAB[LL_DMA_STREAM_3])))->CR, (DMA_SxCR_TCIE | DMA_SxCR_TEIE | DMA_SxCR_DMEIE));
  SET_BIT(((DMA_Stream_TypeDef *)((uint32_t)((uint32_t)DMA2 + STREAM_OFFSET_TAB[LL_DMA_STREAM_3])))->FCR, DMA_SxFCR_FEIE);

  LL_DMA_EnableStream(DMA2, LL_DMA_STREAM_3);
/******* DMA Set end ******/

  /* enable SDIO DMA transfer */
  __SDIO_DMA_ENABLE(SDIO);

  /* Configure the SD DPSM (Data Path State Machine) */
  config.DataTimeOut   = SDMMC_DATATIMEOUT;
  config.DataLength    = BLOCKSIZE * numofblocks;
  config.DataBlockSize = SDIO_DATABLOCK_SIZE_512B;
  config.TransferDir   = SDIO_TRANSFER_DIR_TO_SDIO;
  config.TransferMode  = SDIO_TRANSFER_MODE_BLOCK;
  config.DPSM          = SDIO_DPSM_ENABLE;
  (void)SDIO_ConfigData(SDIO, &config);

  /* send read command */
  if(sdinfo->type != CARD_SDHC_SDXC)
  {
    addr *= 512u;
  }

  if(numofblocks > 1u)
  {
    errorstate = SDMMC_CmdReadMultiBlock(SDIO, addr);
  }
  else
  {
    errorstate = SDMMC_CmdReadSingleBlock(SDIO, addr);
  }

  if(errorstate != SDMMC_ERROR_NONE)
  {
    __SDIO_CLEAR_FLAG(SDIO, SDIO_STATIC_FLAGS);
    sdinfo->error_code |= errorstate;

    return 3;
  }

  /* wait for receive complete */
  
  if(xQueueReceive(sd_drvmsg_qhd, &msg, 5000u) == pdTRUE)
  {
    switch(msg.msgtype)
    {
      case SD_DRVMSG_DMARX_COMPLETE:
        if(numofblocks > 1u)
        {
          errorstate = SDMMC_CmdStopTransfer(SDIO);
          if(errorstate != SDMMC_ERROR_NONE)
          {
            sdinfo->error_code |= errorstate;
          }
        }
        break;

      case SD_DRVMSG_SDIOTRANS_ERROR:
        ret = 4;
        break;

      case SD_DRVMSG_DMARX_ERROR:
        ret = 5;
        break;
        
      default:
        ret = 6;
        break;
    }
  }
  else
  {
    ret = 7;
  }
  
  /* 清除SDIO的静态标志位 */
  __SDIO_CLEAR_FLAG(SDIO, SDIO_STATIC_DATA_FLAGS);
  /* 关闭DMA流 */
  LL_DMA_DisableStream(DMA2, LL_DMA_STREAM_3);
  /* 清除DMA的标志位 */
  WRITE_REG(DMA2->LIFCR , DMA_LIFCR_CTCIF3 | DMA_LIFCR_CHTIF3 | DMA_LIFCR_CTEIF3 | DMA_LIFCR_CDMEIF3 | DMA_LIFCR_CFEIF3);
  /* 关闭DMA中断 */
  CLEAR_BIT(((DMA_Stream_TypeDef *)((uint32_t)((uint32_t)DMA2 + STREAM_OFFSET_TAB[LL_DMA_STREAM_3])))->CR, (DMA_SxCR_TCIE | DMA_SxCR_TEIE | DMA_SxCR_DMEIE));
  CLEAR_BIT(((DMA_Stream_TypeDef *)((uint32_t)((uint32_t)DMA2 + STREAM_OFFSET_TAB[LL_DMA_STREAM_3])))->FCR, DMA_SxFCR_FEIE);
  
  /* 关闭SDIO的DMA请求 */
  SDIO->DCTRL &= (uint32_t)~((uint32_t)SDIO_DCTRL_DMAEN);
  // SDIO->DCTRL &= ~(SDIO_DCTRL_DTEN);

  return ret;
}

/**
  * @brief  Writes Sector(s)
  * @param  lun : not used
  * @param  *buff: Data to be written
  * @param  sector: Sector address (LBA)
  * @param  count: Number of sectors to write (1..128)
  * @retval DRESULT: Operation result
  */
uint8_t sd_diskio_write_blocks(SD_CardInfoType *sdinfo, const uint8_t *pdata, uint32_t blockaddr, uint32_t numofblocks)
{
  SDIO_DataInitTypeDef config;
  SD_DriverMessageStru msg;
  uint32_t errorstate;
  uint32_t addr = blockaddr;
  uint8_t ret;
  uint8_t timeout;

  ret = 0;
  
  /* check parameter */
  if(NULL == pdata)
  {
    sdinfo->error_code |= SDMMC_ERROR_INVALID_PARAMETER;
    return 1;
  }

  sdinfo->error_code = SDMMC_ERROR_NONE;

  if((addr + numofblocks) > (sdinfo->logical_block_nbr))
  {
    sdinfo->error_code |= SDMMC_ERROR_ADDR_OUT_OF_RANGE;
    return 2;
  }

  /* initialize data control register */
  SDIO->DCTRL = 0u;

  /* enable SDIO interrupts */
//  SDIO->MASK = 0;
  __SDIO_ENABLE_IT(SDIO, (SDIO_IT_DCRCFAIL | SDIO_IT_DTIMEOUT | SDIO_IT_TXUNDERR));   

/******** DMA Set ********/
  LL_DMA_SetDataTransferDirection(DMA2, LL_DMA_STREAM_6, LL_DMA_DIRECTION_MEMORY_TO_PERIPH);
  LL_DMA_ConfigAddresses(DMA2, LL_DMA_STREAM_6, (uint32_t)pdata, (uint32_t)&SDIO->FIFO, LL_DMA_DIRECTION_MEMORY_TO_PERIPH);
  LL_DMA_SetDataLength(DMA2, LL_DMA_STREAM_6, (uint32_t)(BLOCKSIZE * numofblocks)/4u);

  /* clear all interrupt flags at correct offset within the register */
  WRITE_REG(DMA2->HIFCR , DMA_HIFCR_CTCIF6 | DMA_HIFCR_CHTIF6 | DMA_HIFCR_CTEIF6 | DMA_HIFCR_CDMEIF6 | DMA_HIFCR_CFEIF6);
  /* enable common interrupts and fifo error interrupt */
  SET_BIT(((DMA_Stream_TypeDef *)((uint32_t)((uint32_t)DMA2 + STREAM_OFFSET_TAB[LL_DMA_STREAM_6])))->CR, (DMA_SxCR_TCIE | DMA_SxCR_TEIE | DMA_SxCR_DMEIE));
//  SET_BIT(((DMA_Stream_TypeDef *)((uint32_t)((uint32_t)DMA2 + STREAM_OFFSET_TAB[LL_DMA_STREAM_6])))->FCR, DMA_SxFCR_FEIE);

  LL_DMA_EnableStream(DMA2, LL_DMA_STREAM_6);
/******* DMA Set end ******/
  
  /* enable SDIO DMA transfer */
  __SDIO_DMA_ENABLE(SDIO);
  
  /* send write command */
  if(sdinfo->type != CARD_SDHC_SDXC)
  {
    addr *= 512u;
  }

  if(numofblocks > 1u)
  {
    errorstate = SDMMC_CmdWriteMultiBlock(SDIO, addr);
  }
  else
  {
    errorstate = SDMMC_CmdWriteSingleBlock(SDIO, addr);
  }

  if(errorstate != SDMMC_ERROR_NONE)
  {
    __SDIO_CLEAR_FLAG(SDIO, SDIO_STATIC_FLAGS);
    sdinfo->error_code |= errorstate;

    return 3;
  }
  
  /* configure the SDIO DPSM(data path state machine) */
  config.DataTimeOut   = SDMMC_DATATIMEOUT;
  config.DataLength    = BLOCKSIZE * numofblocks;
  config.DataBlockSize = SDIO_DATABLOCK_SIZE_512B;
  config.TransferDir   = SDIO_TRANSFER_DIR_TO_CARD;
  config.TransferMode  = SDIO_TRANSFER_MODE_BLOCK;
  config.DPSM          = SDIO_DPSM_ENABLE;
  (void)SDIO_ConfigData(SDIO, &config);

  /* wait for receive complete */
  if(xQueueReceive(sd_drvmsg_qhd, &msg, 5000u) == pdTRUE)
  {
    switch(msg.msgtype)
    {
      case SD_DRVMSG_DMATX_COMPLETE:
        if(numofblocks > 1u)
        {
          errorstate = SDMMC_CmdStopTransfer(SDIO);
          if(errorstate != SDMMC_ERROR_NONE)
          {
            sdinfo->error_code |= errorstate;
          }
        }

        timeout = 200;

        while((SDIO->STA & SDIO_FLAG_DATAEND) == 0)
        {
          if(timeout-- < 1)
          {
            ret = 5;
            break;
          }
          vTaskDelay(1);
        }
        break;

      case SD_DRVMSG_SDIOTRANS_ERROR:
        ret = 6;
        break;

      case SD_DRVMSG_DMATX_ERROR:
        ret = 7;
        break;

      default:
        ret = 8;
        break;
    }
  }
  else
  {
    ret = 9;
  }

  /* 清除SDIO的静态标志位 */
  __SDIO_CLEAR_FLAG(SDIO, SDIO_STATIC_DATA_FLAGS);
  /* 关闭DMA流 */
  LL_DMA_DisableStream(DMA2, LL_DMA_STREAM_6);
  /* 清除DMA的标志位 */
  WRITE_REG(DMA2->HIFCR , DMA_HIFCR_CTCIF6 | DMA_HIFCR_CHTIF6 | DMA_HIFCR_CTEIF6 | DMA_HIFCR_CDMEIF6 | DMA_HIFCR_CFEIF6);
  /* 关闭DMA中断 */
  CLEAR_BIT(((DMA_Stream_TypeDef *)((uint32_t)((uint32_t)DMA2 + STREAM_OFFSET_TAB[LL_DMA_STREAM_6])))->CR, (DMA_SxCR_TCIE | DMA_SxCR_TEIE | DMA_SxCR_DMEIE));
  CLEAR_BIT(((DMA_Stream_TypeDef *)((uint32_t)((uint32_t)DMA2 + STREAM_OFFSET_TAB[LL_DMA_STREAM_6])))->FCR, DMA_SxFCR_FEIE);
  
  /* 关闭SDIO的DMA请求 */
  SDIO->DCTRL &= (uint32_t)~((uint32_t)SDIO_DCTRL_DMAEN);
  // SDIO->DCTRL &= ~(SDIO_DCTRL_DTEN);

  return ret;
}

/**
  * @brief  Erases the specified memory area of the given SD card.
  * @note   This API should be followed by a check on the card state through
  *         HAL_SD_GetCardState().
  * @param  sdinfo: Pointer to SD handle
  * @param  blockstartaddr: Start Block address
  * @param  blockendaddr: End Block address
  * @retval HAL status
  */
uint8_t sd_diskio_erase(SD_CardInfoType *sdinfo, uint32_t blockstartaddr, uint32_t blockendaddr)
{
  uint32_t errorstate;
  uint32_t startaddr = blockstartaddr;
  uint32_t endaddr = blockendaddr;

  sdinfo->error_code = SDMMC_ERROR_NONE;

  if(endaddr < startaddr)
  {
    sdinfo->error_code |= SDMMC_ERROR_INVALID_PARAMETER;
    return 1;
  }

  if(endaddr > (sdinfo->logical_block_nbr))
  {
    sdinfo->error_code |= SDMMC_ERROR_ADDR_OUT_OF_RANGE;
    return 2;
  }

  /* Check if the card command class supports erase command */
  if(((sdinfo->sclass) & SDIO_CCCC_ERASE) == 0U)
  {
    /* Clear all the static flags */
    __SDIO_CLEAR_FLAG(SDIO, SDIO_STATIC_FLAGS);
    sdinfo->error_code |= SDMMC_ERROR_REQUEST_NOT_APPLICABLE;
    return 3;
  }

  if((SDIO_GetResponse(SDIO, SDIO_RESP1) & SDMMC_CARD_LOCKED) == SDMMC_CARD_LOCKED)
  {
    /* Clear all the static flags */
    __SDIO_CLEAR_FLAG(SDIO, SDIO_STATIC_FLAGS);
    sdinfo->error_code |= SDMMC_ERROR_LOCK_UNLOCK_FAILED;
    return 4;
  }

  /* Get start and end block for high capacity cards */
  if(sdinfo->type != CARD_SDHC_SDXC)
  {
    startaddr *= 512u;
    endaddr   *= 512u;
  }

  /* According to sd-card spec 1.0 ERASE_GROUP_START (CMD32) and erase_group_end(CMD33) */
  if(sdinfo->type != CARD_SECURED)
  {
    /* Send CMD32 SD_ERASE_GRP_START with argument as addr  */
    errorstate = SDMMC_CmdSDEraseStartAdd(SDIO, startaddr);
    if(errorstate != SDMMC_ERROR_NONE)
    {
      /* Clear all the static flags */
      __SDIO_CLEAR_FLAG(SDIO, SDIO_STATIC_FLAGS);
      sdinfo->error_code |= errorstate;
      return 5;
    }

    /* Send CMD33 SD_ERASE_GRP_END with argument as addr  */
    errorstate = SDMMC_CmdSDEraseEndAdd(SDIO, endaddr);
    if(errorstate != SDMMC_ERROR_NONE)
    {
      /* Clear all the static flags */
      __SDIO_CLEAR_FLAG(SDIO, SDIO_STATIC_FLAGS);
      sdinfo->error_code |= errorstate;
      return 6;
    }
  }

  /* Send CMD38 ERASE */
  errorstate = SDMMC_CmdErase(SDIO);
  if(errorstate != SDMMC_ERROR_NONE)
  {
    /* Clear all the static flags */
    __SDIO_CLEAR_FLAG(SDIO, SDIO_STATIC_FLAGS);
    sdinfo->error_code |= errorstate;
    return 7;
  }

  return 0;
}


void sd_dma_rx_isr(void)
{
  SD_DriverMessageStru smsg;

  smsg.msgtype = SD_DRVMSG_NULL;
  smsg.errcode = 0;

  if((LL_DMA_IsActiveFlag_TC3(DMA2) != 0) && (LL_DMA_IsEnabledIT_TC(DMA2, LL_DMA_STREAM_3) != 0))
  {
    LL_DMA_ClearFlag_TC3(DMA2);
    smsg.msgtype = SD_DRVMSG_DMARX_COMPLETE;
  }
  
  if((LL_DMA_IsActiveFlag_TE3(DMA2) != 0) && (LL_DMA_IsEnabledIT_TE(DMA2, LL_DMA_STREAM_3) != 0))
  {
    LL_DMA_ClearFlag_TE3(DMA2);
    smsg.msgtype = SD_DRVMSG_DMARX_ERROR;
    smsg.errcode |= SD_DRVMSG_DMARX_TEERR;
  }
  
  if((LL_DMA_IsActiveFlag_FE3(DMA2) != 0) && (LL_DMA_IsEnabledIT_FE(DMA2, LL_DMA_STREAM_3) != 0))
  {
    LL_DMA_ClearFlag_FE3(DMA2);
    smsg.msgtype = SD_DRVMSG_DMARX_ERROR;
    smsg.errcode |= SD_DRVMSG_DMARX_FEERR;
  }

  if((LL_DMA_IsActiveFlag_DME3(DMA2) != 0) && (LL_DMA_IsEnabledIT_DME(DMA2, LL_DMA_STREAM_3) != 0))
  {
    LL_DMA_ClearFlag_DME3(DMA2);
    smsg.msgtype = SD_DRVMSG_DMARX_ERROR;
    smsg.errcode |= SD_DRVMSG_DMARx_DMEERR;
  }

  // if((LL_DMA_IsActiveFlag_HT3(DMA2) != 0) && (LL_DMA_IsEnabledIT_HT(DMA2, LL_DMA_STREAM_3) != 0))
  // {
  //   LL_DMA_ClearFlag_HT3(DMA2);
  // }

  if(smsg.msgtype != SD_DRVMSG_NULL)
  {
    xQueueSendFromISR(sd_drvmsg_qhd, &smsg, NULL);
  }
}

void sd_dma_tx_isr(void)
{
  SD_DriverMessageStru smsg;

  smsg.msgtype = SD_DRVMSG_NULL;
  smsg.errcode = 0;

  if((LL_DMA_IsActiveFlag_TC6(DMA2) != 0) && (LL_DMA_IsEnabledIT_TC(DMA2, LL_DMA_STREAM_6) != 0))
  {
    LL_DMA_ClearFlag_TC6(DMA2);

    smsg.msgtype = SD_DRVMSG_DMATX_COMPLETE;
  }
  
  if((LL_DMA_IsActiveFlag_TE6(DMA2) != 0) && (LL_DMA_IsEnabledIT_TE(DMA2, LL_DMA_STREAM_6) != 0))
  {
    LL_DMA_ClearFlag_TE6(DMA2);

    smsg.msgtype = SD_DRVMSG_DMATX_ERROR;
    smsg.errcode |= SD_DRVMSG_DMATX_TEERR;
  }
  
  if((LL_DMA_IsActiveFlag_FE6(DMA2) != 0) && (LL_DMA_IsEnabledIT_FE(DMA2, LL_DMA_STREAM_6) != 0))
  {
    LL_DMA_ClearFlag_FE6(DMA2);

    smsg.msgtype = SD_DRVMSG_DMATX_ERROR;
    smsg.errcode |= SD_DRVMSG_DMATX_FEERR;
  }
  
  if((LL_DMA_IsActiveFlag_DME6(DMA2) != 0) && (LL_DMA_IsEnabledIT_DME(DMA2, LL_DMA_STREAM_6) != 0))
  {
    LL_DMA_ClearFlag_DME6(DMA2);

    smsg.msgtype = SD_DRVMSG_DMATX_ERROR;
    smsg.errcode |= SD_DRVMSG_DMATX_DMEERR;
  }

  // if((LL_DMA_IsActiveFlag_HT6(DMA2) != 0) && (LL_DMA_IsEnabledIT_HT(DMA2, LL_DMA_STREAM_6) != 0))
  // {
  //   LL_DMA_ClearFlag_HT6(DMA2);
  // }

  if(smsg.msgtype != SD_DRVMSG_NULL)
  {
    xQueueSendFromISR(sd_drvmsg_qhd, &smsg, NULL);
  }
}

void sd_sdio_isr_dma(void)
{
  SD_DriverMessageStru smsg;

  smsg.errcode = 0;

#ifdef SD_DRIVER_USE_IT
  if(__SDIO_GET_FLAG(SDIO, SDIO_FLAG_RXFIFOHF) != RESET)
  {
    SD_Read_IT(hsd);
  }
  
  if(__HAL_SD_GET_FLAG(hsd, SDIO_FLAG_TXFIFOHE) != RESET)
  {
    SD_Write_IT(hsd);
  }
#endif  
  if(__SDIO_GET_FLAG(SDIO, SDIO_FLAG_DATAEND) != RESET)
  {
    __SDIO_CLEAR_FLAG(SDIO, SDIO_FLAG_DATAEND);

    __SDIO_DISABLE_IT(SDIO, SDIO_IT_DATAEND  | SDIO_IT_DCRCFAIL | SDIO_IT_DTIMEOUT |\
                            SDIO_IT_TXUNDERR | SDIO_IT_RXOVERR  | SDIO_IT_TXFIFOHE |\
                            SDIO_IT_RXFIFOHF);

    SDIO->DCTRL &= ~(SDIO_DCTRL_DTEN);
  }

  if(__SDIO_GET_FLAG(SDIO, SDIO_FLAG_DCRCFAIL | SDIO_FLAG_DTIMEOUT | SDIO_FLAG_RXOVERR | SDIO_FLAG_TXUNDERR) != RESET)
  {
    if(__SDIO_GET_FLAG(SDIO, SDIO_FLAG_DCRCFAIL) != RESET)
    {
      smsg.errcode |= SDMMC_ERROR_DATA_CRC_FAIL;
    }
    if(__SDIO_GET_FLAG(SDIO, SDIO_FLAG_DTIMEOUT) != RESET)
    {
      smsg.errcode |= SDMMC_ERROR_DATA_TIMEOUT;
    }
    if(__SDIO_GET_FLAG(SDIO, SDIO_FLAG_RXOVERR) != RESET)
    {
      smsg.errcode |= SDMMC_ERROR_RX_OVERRUN;
    }
    if(__SDIO_GET_FLAG(SDIO, SDIO_FLAG_TXUNDERR) != RESET)
    {
      smsg.errcode |= SDMMC_ERROR_TX_UNDERRUN;
    }

    __SDIO_CLEAR_FLAG(SDIO, SDIO_STATIC_DATA_FLAGS);

    __SDIO_DISABLE_IT(SDIO, SDIO_IT_DATAEND | SDIO_IT_DCRCFAIL | SDIO_IT_DTIMEOUT | SDIO_IT_TXUNDERR | SDIO_IT_RXOVERR);
    
    LL_DMA_DisableStream(DMA2, LL_DMA_STREAM_6);
    LL_DMA_DisableStream(DMA2, LL_DMA_STREAM_3);

    smsg.msgtype = SD_DRVMSG_SDIOTRANS_ERROR;

    xQueueSendFromISR(sd_drvmsg_qhd, &smsg, NULL);
  }
}
